package qq2564874169.tuaotuao.fx;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import qq2564874169.tuaotuao.fx.utils.ExUtil;
import qq2564874169.tuaotuao.fx.utils.ResUtil;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class FxXml implements FxAutoCloseable {
    private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private InputStream xmlstream;
    private Document doc;

    public FxXml(InputStream file) {
        try {
            xmlstream = file;
            DocumentBuilder builder = factory.newDocumentBuilder();
            doc = builder.parse(file);
        } catch (ParserConfigurationException | SAXException | IOException e) {
            throw ExUtil.toRuntime(e);
        }
    }

    public FxXml(String file) {
        this(ResUtil.getStream(file));
    }

    @Override
    public void close() {
        if (xmlstream != null) {
            try {
                xmlstream.close();
                xmlstream = null;
            } catch (IOException e) {
                throw ExUtil.toRuntime(e);
            }
        }
    }

    public Element first(String exp) {
        List<Element> list = find(exp);
        return list.isEmpty() ? null : list.get(0);
    }

    public List<Element> find(String exp) {
        if (exp.startsWith("/")) {
            exp = exp.substring(1);
        }
        List<Element> list = new ArrayList<>();
        List<Element> rslist = new ArrayList<>();
        for (String path : exp.split("/")) {
            if (list.isEmpty()) {
                NodeList roots = doc.getChildNodes();
                for (int i = 0; i < roots.getLength(); i++) {
                    if (roots.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        list.add((Element) roots.item(i));
                    }
                }
            } else {
                for (Element e : list) {
                    NodeList nodes = e.getChildNodes();
                    for (int i = 0; i < nodes.getLength(); i++) {
                        if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                            rslist.add((Element) nodes.item(i));
                        }
                    }
                }
                list.clear();
                list.addAll(rslist);
                rslist.clear();
            }
            if (list.isEmpty()) {
                break;
            }
            for (Element e : list) {
                if (Pattern.matches(path, e.getTagName())) {
                    rslist.add(e);
                }
            }
            list.clear();
            if (rslist.isEmpty()) {
                break;
            } else {
                list.addAll(rslist);
                rslist.clear();
            }
        }
        return list;
    }

    public static String outerXML(Element element) {
        StringBuilder sb = new StringBuilder();
        sb.append("<").append(element.getTagName());
        for (int i = 0; i < element.getAttributes().getLength(); i++) {
            Node node = element.getAttributes().item(i);
            sb.append(" ").append(node.getNodeName()).append("=\"").append(node.getNodeValue()).append("\"");
        }
        sb.append(">");
        for (int i = 0; i < element.getChildNodes().getLength(); i++) {
            Node node = element.getChildNodes().item(i);
            if (node.getNodeType() == Node.TEXT_NODE) {
                sb.append(node.getNodeValue());
            } else if (node.getNodeType() == Node.ELEMENT_NODE) {
                sb.append(outerXML((Element) node));
            }
        }
        sb.append("</").append(element.getTagName()).append(">");
        return sb.toString();
    }
}
